﻿using System;
using GeoAPI.Geometries;

namespace YJKGridTopologyCal.Geometries.Utilities
{
	public class AffineTransformation : ICloneable, ICoordinateSequenceFilter, IEquatable<AffineTransformation>
	{
		public static AffineTransformation ReflectionInstance(double x0, double y0, double x1, double y1)
		{
			AffineTransformation affineTransformation = new AffineTransformation();
			affineTransformation.SetToReflection(x0, y0, x1, y1);
			return affineTransformation;
		}

		public static AffineTransformation ReflectionInstance(double x, double y)
		{
			AffineTransformation affineTransformation = new AffineTransformation();
			affineTransformation.SetToReflection(x, y);
			return affineTransformation;
		}

		public static AffineTransformation RotationInstance(double theta)
		{
			return AffineTransformation.RotationInstance(Math.Sin(theta), Math.Cos(theta));
		}

		public static AffineTransformation RotationInstance(double sinTheta, double cosTheta)
		{
			AffineTransformation affineTransformation = new AffineTransformation();
			affineTransformation.SetToRotation(sinTheta, cosTheta);
			return affineTransformation;
		}

		public static AffineTransformation RotationInstance(double theta, double x, double y)
		{
			return AffineTransformation.RotationInstance(Math.Sin(theta), Math.Cos(theta), x, y);
		}

		public static AffineTransformation RotationInstance(double sinTheta, double cosTheta, double x, double y)
		{
			AffineTransformation affineTransformation = new AffineTransformation();
			affineTransformation.SetToRotation(sinTheta, cosTheta, x, y);
			return affineTransformation;
		}

		public static AffineTransformation ScaleInstance(double xScale, double yScale)
		{
			AffineTransformation affineTransformation = new AffineTransformation();
			affineTransformation.SetToScale(xScale, yScale);
			return affineTransformation;
		}

		public static AffineTransformation ScaleInstance(double xScale, double yScale, double x, double y)
		{
			AffineTransformation affineTransformation = new AffineTransformation();
			affineTransformation.Translate(-x, -y);
			affineTransformation.Scale(xScale, yScale);
			affineTransformation.Translate(x, y);
			return affineTransformation;
		}

		public static AffineTransformation ShearInstance(double xShear, double yShear)
		{
			AffineTransformation affineTransformation = new AffineTransformation();
			affineTransformation.SetToShear(xShear, yShear);
			return affineTransformation;
		}

		public static AffineTransformation TranslationInstance(double x, double y)
		{
			AffineTransformation affineTransformation = new AffineTransformation();
			affineTransformation.SetToTranslation(x, y);
			return affineTransformation;
		}

		public AffineTransformation()
		{
			this.SetToIdentity();
		}

		public AffineTransformation(double[] matrix)
		{
			this._m00 = matrix[0];
			this._m01 = matrix[1];
			this._m02 = matrix[2];
			this._m10 = matrix[3];
			this._m11 = matrix[4];
			this._m12 = matrix[5];
		}

		public AffineTransformation(double m00, double m01, double m02, double m10, double m11, double m12)
		{
			this.SetTransformation(m00, m01, m02, m10, m11, m12);
		}

		public AffineTransformation(AffineTransformation trans)
		{
			this.SetTransformation(trans);
		}

		public AffineTransformation(Coordinate src0, Coordinate src1, Coordinate src2, Coordinate dest0, Coordinate dest1, Coordinate dest2) : this(new AffineTransformationBuilder(src0, src1, src2, dest0, dest1, dest2).GetTransformation())
		{
		}

		public AffineTransformation SetToIdentity()
		{
			this._m00 = 1.0;
			this._m01 = 0.0;
			this._m02 = 0.0;
			this._m10 = 0.0;
			this._m11 = 1.0;
			this._m12 = 0.0;
			return this;
		}

		public AffineTransformation SetTransformation(double m00, double m01, double m02, double m10, double m11, double m12)
		{
			this._m00 = m00;
			this._m01 = m01;
			this._m02 = m02;
			this._m10 = m10;
			this._m11 = m11;
			this._m12 = m12;
			return this;
		}

		public AffineTransformation SetTransformation(AffineTransformation trans)
		{
			this._m00 = trans._m00;
			this._m01 = trans._m01;
			this._m02 = trans._m02;
			this._m10 = trans._m10;
			this._m11 = trans._m11;
			this._m12 = trans._m12;
			return this;
		}

		public double[] MatrixEntries
		{
			get
			{
				return new double[]
				{
					this._m00,
					this._m01,
					this._m02,
					this._m10,
					this._m11,
					this._m12
				};
			}
		}

		public double Determinant
		{
			get
			{
				return this._m00 * this._m11 - this._m01 * this._m10;
			}
		}

		public AffineTransformation GetInverse()
		{
			double determinant = this.Determinant;
			if (determinant == 0.0)
			{
				throw new NoninvertibleTransformationException("Transformation is non-invertible");
			}
			double m = this._m11 / determinant;
			double m2 = -this._m10 / determinant;
			double m3 = -this._m01 / determinant;
			double m4 = this._m00 / determinant;
			double m5 = (this._m01 * this._m12 - this._m02 * this._m11) / determinant;
			double m6 = (-this._m00 * this._m12 + this._m10 * this._m02) / determinant;
			return new AffineTransformation(m, m3, m5, m2, m4, m6);
		}

		public AffineTransformation SetToReflectionBasic(double x0, double y0, double x1, double y1)
		{
			if (x0 == x1 && y0 == y1)
			{
				throw new ArgumentException("Reflection line points must be distinct");
			}
			double num = x1 - x0;
			double num2 = y1 - y0;
			double num3 = Math.Sqrt(num * num + num2 * num2);
			double num4 = num2 / num3;
			double num5 = num / num3;
			double num6 = 2.0 * num4 * num5;
			double num7 = num5 * num5 - num4 * num4;
			this._m00 = num7;
			this._m01 = num6;
			this._m02 = 0.0;
			this._m10 = num6;
			this._m11 = -num7;
			this._m12 = 0.0;
			return this;
		}

		public AffineTransformation SetToReflection(double x0, double y0, double x1, double y1)
		{
			if (x0 == x1 && y0 == y1)
			{
				throw new ArgumentException("Reflection line points must be distinct");
			}
			this.SetToTranslation(-x0, -y0);
			double num = x1 - x0;
			double num2 = y1 - y0;
			double num3 = Math.Sqrt(num * num + num2 * num2);
			double num4 = num2 / num3;
			double cosTheta = num / num3;
			this.Rotate(-num4, cosTheta);
			this.Scale(1.0, -1.0);
			this.Rotate(num4, cosTheta);
			this.Translate(x0, y0);
			return this;
		}

		public AffineTransformation SetToReflection(double x, double y)
		{
			if (x == 0.0 && y == 0.0)
			{
				throw new ArgumentException("Reflection vector must be non-zero");
			}
			if (x == y)
			{
				this._m00 = 0.0;
				this._m01 = 1.0;
				this._m02 = 0.0;
				this._m10 = 1.0;
				this._m11 = 0.0;
				this._m12 = 0.0;
				return this;
			}
			double num = Math.Sqrt(x * x + y * y);
			double num2 = y / num;
			double cosTheta = x / num;
			this.Rotate(-num2, cosTheta);
			this.Scale(1.0, -1.0);
			this.Rotate(num2, cosTheta);
			return this;
		}

		public AffineTransformation SetToRotation(double theta)
		{
			this.SetToRotation(Math.Sin(theta), Math.Cos(theta));
			return this;
		}

		public AffineTransformation SetToRotation(double sinTheta, double cosTheta)
		{
			this._m00 = cosTheta;
			this._m01 = -sinTheta;
			this._m02 = 0.0;
			this._m10 = sinTheta;
			this._m11 = cosTheta;
			this._m12 = 0.0;
			return this;
		}

		public AffineTransformation SetToRotation(double theta, double x, double y)
		{
			this.SetToRotation(Math.Sin(theta), Math.Cos(theta), x, y);
			return this;
		}

		public AffineTransformation SetToRotation(double sinTheta, double cosTheta, double x, double y)
		{
			this._m00 = cosTheta;
			this._m01 = -sinTheta;
			this._m02 = x - x * cosTheta + y * sinTheta;
			this._m10 = sinTheta;
			this._m11 = cosTheta;
			this._m12 = y - x * sinTheta - y * cosTheta;
			return this;
		}

		public AffineTransformation SetToScale(double xScale, double yScale)
		{
			this._m00 = xScale;
			this._m01 = 0.0;
			this._m02 = 0.0;
			this._m10 = 0.0;
			this._m11 = yScale;
			this._m12 = 0.0;
			return this;
		}

		public AffineTransformation SetToShear(double xShear, double yShear)
		{
			this._m00 = 1.0;
			this._m01 = xShear;
			this._m02 = 0.0;
			this._m10 = yShear;
			this._m11 = 1.0;
			this._m12 = 0.0;
			return this;
		}

		public AffineTransformation SetToTranslation(double dx, double dy)
		{
			this._m00 = 1.0;
			this._m01 = 0.0;
			this._m02 = dx;
			this._m10 = 0.0;
			this._m11 = 1.0;
			this._m12 = dy;
			return this;
		}

		public AffineTransformation Reflect(double x0, double y0, double x1, double y1)
		{
			this.Compose(AffineTransformation.ReflectionInstance(x0, y0, x1, y1));
			return this;
		}

		public AffineTransformation Reflect(double x, double y)
		{
			this.Compose(AffineTransformation.ReflectionInstance(x, y));
			return this;
		}

		public AffineTransformation Rotate(double theta)
		{
			this.Compose(AffineTransformation.RotationInstance(theta));
			return this;
		}

		public AffineTransformation Rotate(double sinTheta, double cosTheta)
		{
			this.Compose(AffineTransformation.RotationInstance(sinTheta, cosTheta));
			return this;
		}

		public AffineTransformation Rotate(double theta, double x, double y)
		{
			this.Compose(AffineTransformation.RotationInstance(theta, x, y));
			return this;
		}

		public AffineTransformation Rotate(double sinTheta, double cosTheta, double x, double y)
		{
			this.Compose(AffineTransformation.RotationInstance(sinTheta, cosTheta));
			return this;
		}

		public AffineTransformation Scale(double xScale, double yScale)
		{
			this.Compose(AffineTransformation.ScaleInstance(xScale, yScale));
			return this;
		}

		public AffineTransformation Shear(double xShear, double yShear)
		{
			this.Compose(AffineTransformation.ShearInstance(xShear, yShear));
			return this;
		}

		public AffineTransformation Translate(double x, double y)
		{
			this.Compose(AffineTransformation.TranslationInstance(x, y));
			return this;
		}

		public AffineTransformation Compose(AffineTransformation trans)
		{
			double m = trans._m00 * this._m00 + trans._m01 * this._m10;
			double m2 = trans._m00 * this._m01 + trans._m01 * this._m11;
			double m3 = trans._m00 * this._m02 + trans._m01 * this._m12 + trans._m02;
			double m4 = trans._m10 * this._m00 + trans._m11 * this._m10;
			double m5 = trans._m10 * this._m01 + trans._m11 * this._m11;
			double m6 = trans._m10 * this._m02 + trans._m11 * this._m12 + trans._m12;
			this._m00 = m;
			this._m01 = m2;
			this._m02 = m3;
			this._m10 = m4;
			this._m11 = m5;
			this._m12 = m6;
			return this;
		}

		public AffineTransformation ComposeBefore(AffineTransformation trans)
		{
			double m = this._m00 * trans._m00 + this._m01 * trans._m10;
			double m2 = this._m00 * trans._m01 + this._m01 * trans._m11;
			double m3 = this._m00 * trans._m02 + this._m01 * trans._m12 + this._m02;
			double m4 = this._m10 * trans._m00 + this._m11 * trans._m10;
			double m5 = this._m10 * trans._m01 + this._m11 * trans._m11;
			double m6 = this._m10 * trans._m02 + this._m11 * trans._m12 + this._m12;
			this._m00 = m;
			this._m01 = m2;
			this._m02 = m3;
			this._m10 = m4;
			this._m11 = m5;
			this._m12 = m6;
			return this;
		}

		public Coordinate Transform(Coordinate src, Coordinate dest)
		{
			double x = this._m00 * src.X + this._m01 * src.Y + this._m02;
			double y = this._m10 * src.X + this._m11 * src.Y + this._m12;
			dest.X = x;
			dest.Y = y;
			return dest;
		}

		public IGeometry Transform(IGeometry g)
		{
			IGeometry geometry = (IGeometry)g.Clone();
			geometry.Apply(this);
			return geometry;
		}

		public void Transform(ICoordinateSequence seq, int i)
		{
			double value = this._m00 * seq.GetOrdinate(i, Ordinate.X) + this._m01 * seq.GetOrdinate(i, Ordinate.Y) + this._m02;
			double value2 = this._m10 * seq.GetOrdinate(i, Ordinate.X) + this._m11 * seq.GetOrdinate(i, Ordinate.Y) + this._m12;
			seq.SetOrdinate(i, Ordinate.X, value);
			seq.SetOrdinate(i, Ordinate.Y, value2);
		}

		public void Filter(ICoordinateSequence seq, int i)
		{
			this.Transform(seq, i);
		}

		public bool GeometryChanged
		{
			get
			{
				return true;
			}
		}

		public bool Done
		{
			get
			{
				return false;
			}
		}

		public bool IsIdentity
		{
			get
			{
				return this._m00 == 1.0 && this._m01 == 0.0 && this._m02 == 0.0 && this._m10 == 0.0 && this._m11 == 1.0 && this._m12 == 0.0;
			}
		}

		public override bool Equals(object obj)
		{
			return this.Equals(obj as AffineTransformation);
		}

		public bool Equals(AffineTransformation trans)
		{
			return trans != null && (this._m00 == trans._m00 && this._m01 == trans._m01 && this._m02 == trans._m02 && this._m10 == trans._m10 && this._m11 == trans._m11) && this._m12 == trans._m12;
		}

		public override string ToString()
		{
			return string.Concat(new object[]
			{
				"AffineTransformation[[",
				this._m00,
				", ",
				this._m01,
				", ",
				this._m02,
				"], [",
				this._m10,
				", ",
				this._m11,
				", ",
				this._m12,
				"]]"
			});
		}

		public object Clone()
		{
			object result;
			try
			{
				result = base.MemberwiseClone();
			}
			catch (Exception)
			{
				result = new AffineTransformation(this);
			}
			return result;
		}

		private double _m00;

		private double _m01;

		private double _m02;

		private double _m10;

		private double _m11;

		private double _m12;
	}
}
